package com.projectm.login.controller;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.framework.common.AjaxResult;
import com.framework.common.ResultCode;
import com.framework.common.constant.Constants;
import com.framework.common.exception.CustomException;
import com.framework.common.utils.DateUtils;
import com.framework.common.utils.security.Md5Utils;
import com.framework.security.util.RedisCache;
import com.framework.security.util.TokenUtil;
import com.framework.security.util.UserUtil;
import com.framework.utils.PinYinUtil;
import com.projectm.common.Constant;
import com.projectm.common.RedisUtil;
import com.projectm.common.StringUtil;
import com.projectm.config.MProjectConfig;
import com.projectm.login.entity.LoginUser;
import com.projectm.login.service.LoginService;
import com.projectm.member.domain.Member;
import com.projectm.member.domain.MemberAccount;
import com.projectm.member.mapper.MemberAccountMapper;
import com.projectm.member.service.MemberAccountService;
import com.projectm.member.service.MemberService;
import com.projectm.org.domain.Organization;
import com.projectm.org.service.OrganizationService;
import com.projectm.project.domain.ProjectAuth;
import com.projectm.project.domain.ProjectAuthNode;
import com.projectm.project.domain.ProjectNode;
import com.projectm.project.mapper.ProjectAuthMapper;
import com.projectm.project.service.ProjectAuthNodeService;
import com.projectm.project.service.ProjectAuthService;
import com.projectm.project.service.ProjectNodeService;
import com.projectm.system.domain.Notify;
import com.projectm.system.domain.NotifyTo;
import com.projectm.system.mapper.NotifyMapper;
import com.projectm.task.mapper.TaskMapper;
import com.projectm.task.service.TaskService;
import com.sun.org.apache.bcel.internal.generic.NEW;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.implementation.bytecode.Throw;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.swing.text.DateFormatter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@RestController
public class LoginController {


    @Value("${mproject.projectImg}")
    private String projectImg;
    @Value("${mproject.userImg}")
    private String userImg;
    @Resource
    private AuthenticationManager authenticationManager;


    @Value("${jwt.expiration}")
    private int expireTime;
    @Value("${mproject.downloadServer}")
    private String downloadServer;

    @Autowired
    private LoginService loginService;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private TokenUtil tokenUtil;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private MemberAccountService memberAccountService;
    @Autowired
    private ProjectAuthService projectAuthService;
    @Autowired
    private ProjectAuthNodeService projectAuthNodeService;
    @Autowired
    private ProjectNodeService projectNodeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private NotifyMapper notifyMapper;
    @Autowired
    private MemberAccountMapper memberAccountMapper;
    @Autowired
    private ProjectAuthMapper projectAuthMapper;
    /**
     * 登录方法
     *
     * @param account  用户名
     * @param password 密码
     * @return 结果
     */
    @PostMapping("/teamwork/login")
    @ResponseBody
    public AjaxResult login(String account, String password, String mobile, String captcha) {
        System.out.println("\n\n\n账号" + account + "#密码" + password);
        System.out.println("\n\n\n手机号" + mobile + "#验证码" + captcha);
        LoginUser loginUser = new LoginUser();
        Object redisCaptcha = null;
        if (mobile != null) {
            redisCaptcha = redisCache.getCacheObject(mobile);
            if (redisCaptcha == null) {
                return AjaxResult.error("验证码已过期!请重新发送验证码.");
            }
        }
        // 手机验证码登录
        if (redisCaptcha != null && redisCaptcha.toString().equals(captcha)) {
            QueryWrapper<Member> wrapper = new QueryWrapper<>();
            wrapper.eq("mobile", mobile);
            Member member = memberService.getOne(wrapper);
            if (member == null) {
                return new AjaxResult(ResultCode.USER_ERROR_EXCEPTION);
            }
            loginUser.setUser(member);
        } else {
            // 账号密码登录
            QueryWrapper<Member> wrapper = new QueryWrapper<>();
            boolean isEmail = StringUtil.isEmail(account);
            wrapper.eq(!isEmail, "account", account).
                    eq(isEmail, "email", account);
            Member member = memberService.getOne(wrapper);
            if (member == null) {
                return new AjaxResult(ResultCode.USER_ERROR_EXCEPTION);
            }
            loginUser.setUser(member);
//            Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(account, password));
//            loginUser = (LoginUser) authentication.getPrincipal();
        }

        System.out.println("\n\n#######\n" + loginUser);
        String userCode = loginUser.getUser().getCode();
        // 查询组织成员
        List<MemberAccount> list = memberAccountService.lambdaQuery().eq(MemberAccount::getMember_code, userCode).list();
        loginUser.getUser().setMemberAccountList(list);
        // 查询权限
        Set<String> authSet = list.stream().map(MemberAccount::getAuthorize).collect(Collectors.toSet());
        System.out.println("authSet:" + authSet);
        List<ProjectAuthNode> projectAuthNodeList = projectAuthNodeService.lambdaQuery().
                in(ProjectAuthNode::getAuth, authSet).list();
        list.forEach(memberAccount -> {
            List<String> nodeList = projectAuthNodeList.parallelStream().filter(auth -> Objects.equals(
                    auth.getAuth().toString(), memberAccount.getAuthorize()))
                    .map(ProjectAuthNode::getNode).collect(Collectors.toList());
            memberAccount.setNodeList(nodeList);
        });
        // 存储到缓存
        redisCache.setCacheObject(Constants.LOGIN_USER_KEY + userCode, loginUser, expireTime, TimeUnit.MINUTES);
        // 生成token
        Map<String, Object> tokenList = buildToken(userCode);
        Set<String> collect = list.stream().map(MemberAccount::getOrganization_code).collect(Collectors.toSet());
        List<Organization> organizationList = organizationService.lambdaQuery().in(Organization::getCode, collect).list();
        Map<String, Object> resultMap = new HashMap<>(8);
        resultMap.put("member", loginUser.getUser());
        resultMap.put("organizationList", organizationList);
        resultMap.put("tokenList", tokenList);
        resultMap.put("userCode", userCode);
        // 获取所有今天截止的任务
        List<NotifyTo> myAllUnDone = taskMapper.getMyAllUnDone(userCode);
        List<String> tempList = new ArrayList<>();
        tempList.add(userCode);
        // 遍历每一个未完成的且有截止日期的任务, 看有木有通知过一次，没有通知过就通知
        for (NotifyTo notifyTo : myAllUnDone) {
            String tempTitle;
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime dateTime = LocalDateTime.parse(notifyTo.getEndTime()+":00", formatter);
            // 逾期任务通知, 截止时间小于当前时间
            if (dateTime.isBefore(LocalDateTime.now())) {
                tempTitle = "已经逾期任务";
            // 今天截止任务通知
            } else {
                tempTitle = "今日截止任务";
            }
            // 查看有木有通知过
            QueryWrapper<Notify> wrapper = new QueryWrapper<>();
            wrapper.eq("source_code", notifyTo.getTaskCode()).
                    eq("title", "系统: "+ tempTitle).
                    eq("`to`", userCode);
            List<Notify> notifies = notifyMapper.selectList(wrapper);
            LocalDateTime lastNotifyTime = null;
            // 找最后一次通知时间，
            for (Notify notify : notifies) {
                if (lastNotifyTime == null) {
                    lastNotifyTime = LocalDateTime.parse(notify.getCreate_time(), formatter);
                    continue;
                }
                LocalDateTime createTime = LocalDateTime.parse(notify.getCreate_time(), formatter);
                if (createTime.isAfter(lastNotifyTime)) {
                    lastNotifyTime = createTime;
                }
            }
            // 判断有木有通知过，如果通知过了，而且是今天通知过了，就不通知了
            if (lastNotifyTime!=null && lastNotifyTime.getDayOfYear() == LocalDateTime.now().getDayOfYear()) {
                continue;
            }
            // 没通知过就通知
            taskService.noticeNotify(notifyTo.getTaskCode(), notifyTo.getContent(),true,
                    tempTitle, "message", "system", tempList);
        }
        return AjaxResult.success(resultMap);
    }

    /**
     * 获取验证码
     *
     * @param mobile 手机号
     * @return 验证码
     */
    @PostMapping("/project/login/getCaptcha")
    @ResponseBody
    public AjaxResult getCaptcha(String mobile, Boolean isRegister) {
        QueryWrapper<Member> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile", mobile);
        Member member = memberService.getOne(wrapper);
        // 判断是否是注册，如果是正常就判断是不是已存在
        if (isRegister != null) {
            if (isRegister && member != null) {
                return AjaxResult.error("该手机号已被注册!");
            }
        } else {
            // 非注册的话，判断是否存在该手机号
            if (member == null) {
                return AjaxResult.error("该手机号还未注册!");
            }
        }
        System.out.println("\n\n\n>>>>>>>验证码>>>>>>>>\n\n\n");
        String captcha = String.valueOf((int)((Math.random() * 9 + 1) * 100000));
        redisUtil.setVerificationKey(mobile, captcha, Constants.CAPTCHA_EXPIRATION);
//        redisCache.setCacheObject(mobile, captcha, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        return AjaxResult.success(Integer.valueOf(captcha));
    }
    /**
     * 刷新Token
     * @param refreshToken token值
     * @return
     */
    @PostMapping("/index/index/refreshAccessToken")
    @ResponseBody
    public AjaxResult login(String refreshToken) {
        if (tokenUtil.verifyToken(refreshToken)) {
            String userCode = tokenUtil.getUserCode(refreshToken);
            boolean expire = redisCache.expire(Constants.LOGIN_USER_KEY + userCode, 1, TimeUnit.DAYS);
            log.info("更新缓存时间：{}", expire);
            // 生成token
            Map<String, Object> tokenList = buildToken(userCode);
            return AjaxResult.success(tokenList);
        } else {
            throw new CustomException(ResultCode.VERIFY_TOKEN_FAIL);
        }
    };

    /**
     * 构建Token信息
     * @param userCode 用户Code
     * @return
     */
    private Map<String, Object> buildToken(String userCode) {

        Calendar instance = Calendar.getInstance();
        //
        Date issDate = instance.getTime();
        instance.add(Calendar.MINUTE, expireTime);
        Date expireDate = instance.getTime();
        String token = tokenUtil.createToken(issDate, expireDate, userCode);
        instance.add(Calendar.MINUTE, 10);
        Date expireDate1 = instance.getTime();
        String refreshToken = tokenUtil.createToken(issDate, expireDate1, userCode);
        Map<String, Object> tokenList = new HashMap<>(10);
        Calendar.getInstance();
        tokenList.put("accessToken", token);
        tokenList.put("refreshToken", refreshToken);
        tokenList.put("tokenType", Constants.TOKEN_PREFIX.trim());
        tokenList.put("accessTokenExp", expireDate.getTime()/1000);
        return tokenList;
    }


    /**
     * 注册用户
     *
     * @param mobile 手机号
     * @return 验证码
     */
    @PostMapping("/project/login/register")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult register(String email, String name, String password, String mobile, String captcha) {
        System.out.println(">begin");
        System.out.println(">");
        System.out.println(">");
        System.out.println(">");
        String downloadUrl = "/common/image?filePathName="+ Constant.PHOTO_SHOW_PATH +"&realFileName="+userImg;
        // 手机虚拟验证码
        String redisCaptcha = redisUtil.getValue(mobile);
        if (redisCaptcha != null && !redisCaptcha.equals(captcha)) {
            return new AjaxResult(ResultCode.CAPTCHA_EXPIRED);
        }
        // 查询是否邮箱重复的用户
        if (memberService.count(new QueryWrapper<Member>().eq("email", email)) > 0) {
            return AjaxResult.error("邮箱已经被注册！");
        }

        String account = PinYinUtil.getPinyin(name) + "_" + IdUtil.fastSimpleUUID().substring(0, 7);

        // 生成的组织ID
        String uuid = IdUtil.fastSimpleUUID();
        // 用户
        Member saveMember = Member.builder().code(uuid).account(account).password(Md5Utils.hash(password)).name(name).mobile(mobile)
                .create_time(LocalDateTime.now().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)))
                .status(1).avatar(downloadServer + downloadUrl).email(email).build();
        // 组织
        Organization saveOrganization = Organization.builder().name(name + "的个人项目").owner_code(uuid)
                .create_time(LocalDateTime.now().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)))
                .personal(1).code(IdUtil.fastSimpleUUID()).build();
        // TODO 设置系统管理员 统一设置角色类型
        //组织角色 设置写死的两种角色
        ProjectAuth auth1 = ProjectAuth.builder().title("管理员").status(1).sort(0).desc("管理员")
                .create_at(LocalDateTime.now().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)))
                .organization_code(saveOrganization.getCode()).type("admin").build();
//        ProjectAuth auth2 = ProjectAuth.builder().title("用户").status(1).sort(1).desc("用户")
//                .create_at(LocalDateTime.now().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)))
//                .organization_code(saveOrganization.getCode()).type("member").is_default(1).build();
        boolean saveAuth1 = projectAuthService.save(auth1);
//        boolean saveAuth2 = projectAuthService.save(auth2);
//        log.info("创建用户：{}",  saveAuth2);
        log.info("创建管理员：{}", saveAuth1);
        // 组织成员账户 来绑定组织
        MemberAccount saveMemberAccount = MemberAccount.builder().code(IdUtil.fastSimpleUUID()).member_code(saveMember.getCode())
                .organization_code(saveOrganization.getCode()).authorize(auth1.getId().toString()).is_owner(1).name(name).mobile(mobile).email(email)
                .create_time(LocalDateTime.now().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)))
                .status(1).avatar(saveMember.getAvatar()).build();
        // TODO 权限管理
        // 项目管理员权限节点（所有权限
        System.out.println("\n\n-----------------------------");
        List<ProjectNode> adminNode = projectNodeService.lambdaQuery().list();
        // 用户权限 - 过滤出管理员权限
//        List<ProjectNode> memberNode = adminNode.parallelStream().filter(
//                o -> o.getNode().contains("project/notify") ||
//                        o.getNode().contains("project/project") ||
//                        o.getNode().contains("project/task")
//        ).collect(Collectors.toList());
        System.out.println("adminNode:" + adminNode);
//        System.out.println("memberNode:" + memberNode);
        // 当前创建用户的角色对应的权限节点集合
        List<ProjectAuthNode> saveAuthNode = new ArrayList<>();
        adminNode.forEach(o -> {
            saveAuthNode.add(ProjectAuthNode.builder().
                    auth(auth1.getId()).
                    node(o.getNode()).build());
        });
//        memberNode.forEach(o -> {
//            saveAuthNode.add(ProjectAuthNode.builder().
//                    auth(auth2.getId()).
//                    node(o.getNode()).build());
//        });
        System.out.println("saveAuthNode:" + saveAuthNode);
        System.out.println("-----------------------------\n\n\n");
        boolean b = memberService.save(saveMember);
        boolean b1 = organizationService.save(saveOrganization);
        // 保存权限
        boolean b2 = projectAuthNodeService.saveBatch(saveAuthNode);
        boolean b3 = memberAccountService.save(saveMemberAccount);
        log.info("新建用户：{}，新建组织：{}，新建角色节点：{}，组织用户关系：{}", b, b1, b2, b3);
        System.out.println(">end");
        System.out.println(">");
        System.out.println(">");
        System.out.println(">");
        return AjaxResult.success();
    }


    /**
     * 绑定号码
     * @param mobile 号码
     * @param captcha 验证码
     * @return
     */
    @PostMapping("/project/login/_bindMobile")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult bindMobile(String mobile, String captcha) {
        Object cacheObject = redisCache.getCacheObject(mobile);
        if (cacheObject != null && StrUtil.equals(captcha, cacheObject.toString())) {
            LoginUser loginUser = UserUtil.getLoginUser();
            Member one = memberService.lambdaQuery().select(Member::getName).eq(Member::getMobile, mobile).one();
            if (ObjectUtil.isEmpty(one)) {
                boolean update = memberService.lambdaUpdate().set(Member::getMobile, mobile).eq(Member::getCode, loginUser.getUser().getCode()).update();
                log.info("电话号码修改：{}", update);
                String str = update ? "绑定成功" : "绑定失败";
                return AjaxResult.success(str, update);
            } else {
                return new AjaxResult(ResultCode.MOBILE_USED);
            }
        } else {
            return new AjaxResult(ResultCode.CAPTCHA_EXPIRED);
        }
    }

    @PostMapping("/project/login/_bindMail")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult bindMobile(String mail) {
        LoginUser loginUser = UserUtil.getLoginUser();
        Member one = memberService.lambdaQuery().select(Member::getName).eq(Member::getEmail, mail).one();
        if (ObjectUtil.isEmpty(one)) {
            boolean update = memberService.lambdaUpdate().set(Member::getEmail, mail).eq(Member::getCode, loginUser.getUser().getCode()).update();
            log.info("邮箱修改：{}", update);
            String str = update ? "绑定成功" : "绑定失败";
            return AjaxResult.success(str, update);
        } else {
            return new AjaxResult(ResultCode.EMAIL_USED);
        }
    }
    
    @PostMapping("/project/login/_out")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult logOut(String userCode) {
        System.out.println("USER:" + redisUtil.getValue(Constants.LOGIN_USER_KEY + userCode));
        redisUtil.removeKey(Constants.LOGIN_USER_KEY + userCode);
        return AjaxResult.success("退出成功", true);
    }


    /**
     * 修改密码
     * @param id 用户主键
     * @param password 旧密码
     * @param newPassword 新密码
     * @return
     */
    @PostMapping("/project/index/editPassword")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult editPassword(String id, String password, String newPassword) {
        Member member = memberService.lambdaQuery().select(Member::getPassword).eq(Member::getId, id).one();
        // 先判断原密码是否正确
        if (StrUtil.equals(member.getPassword(), Md5Utils.hash(password))) {
            boolean update = memberService.lambdaUpdate().set(Member::getPassword, Md5Utils.hash(newPassword)).eq(Member::getId, id).update();
            log.info("密码修改：{}", update);
            String str = update ? "密码修改成功" : "密码修改失败";
            return AjaxResult.success(str, update);
        } else {
            return AjaxResult.warn("原密码错误！");
        }
    }

    @ApiOperation(value = "通过邮箱重置用户密码")
    @PostMapping("/project/login/_resetPasswordByMail")
    @ResponseBody
    public AjaxResult resetPwdByEmail(@ApiParam(value = "email", required = true) @RequestParam String email,
                                      @ApiParam(value = "新密码", required = true) @RequestParam String newPwd,
                                      @ApiParam(value = "邮箱验证码", required = true) @RequestParam String verifyCode,
                                      @ApiParam(value = "UUID", required = true) @RequestParam String UUIDKey) {
        return memberService.resetPwdByEmail(email, newPwd, verifyCode, UUIDKey);
    }


    @ApiOperation(value = "发送邮箱验证码")
    @PostMapping("/project/login/_getMailCaptcha")
    @ResponseBody
    public AjaxResult sentEmailVerifyCode(@ApiParam(value = "接受者邮箱", required = true) @RequestParam String recipientEmail) {
        return memberService.sentEmailVerifyCode(recipientEmail);
    }

}
